
    TestCase
    (
        'es5.PropertyDescriptorsTestCase'
        
    ,   {
			setUp: function ()
			{
				this.log= function (message)
				{
					if (window.console && console.log)
						console.log(message);
				}
			}	
			
		,	testEnvironment: function ()
			{
				assertUndefined('ObjectKit extensions are not defined', Function.prototype.is);
				assertInstanceOf('ES5 extensions to Object are available', Function, Object.defineProperties);
				
				var extensions=
					[
						'defineProperties'
					,	'defineProperty'
					,	'isExtensible'
					,	'preventExtensions'
					,	'freeze'
					,	'isFrozen'
					,	'seal'
					,	'isSealed'
					,	'getOwnPropertyDescriptor'
					]
					
				,	unsupported= []
				,	supported= []
				
				for (var i= 0, n= extensions.length; i < n; i++)
				{
					if ('function' !== typeof Object[extensions[i]])
						unsupported.push(extensions[i]);
					else
						supported.push(extensions[i])
				}
				
				

				assertSame('No extensions are unsupported: ', 0, unsupported.length);
				if (0 !== unsupported.length)
					throw 'Unsupported: \n' + unsupported.join('\n');
				
			}
			
		,	testPropertyDescriptors: function ()
			{
				var error
				,	esObject
				,	value= 
					{
						toString: function(){return 'value'}
					}
				
				try
				{
					Object.defineProperties
					(
						EsFiveObject.prototype
					
					,	{
							/* old style properties cannot be added in this way */
							_property: null
						}
					);
				}
				catch (thrown)
				{
					this.log('Error thrown defining old school properties: ' + thrown);
					error= thrown;
				}
				
				assertInstanceOf('An error was thrown defining old school property via #defineProperties', Error, error);
				
				Object.defineProperties
				(
					EsFiveObject.prototype
				
				,	{
						/* 
						 * An accessor descriptor. Accessor descriptors
						 * can have get, set, enumerable and configurable,
						 * but not value or writable
						 */
						property:
						{
							get: function ()
							{
								return this.getProperty();
							}
							
						,	set: function (value)
							{
								this.setProperty(value);
							}	
						}
						
					,	getProperty:
						{
							/* use a 'data descriptor' for methods */
							
							value: function ()
							{
								console.log('getting property');
								return this.propertyValue;
							}
						}
						
					,	setProperty:
						{
							value: function (value)
							{
								console.log('setting property: ' + value);
								this.propertyValue= value;
							}
						}
					}
				);
				
				// define an old school property
				Object.prototype.propertyValue= true;
				
				// finalize the class definition
				Object.seal(EsFiveObject.prototype);
				
				function EsFiveObject (value)
				{
					this.property= value;
				};
				
				error= null
				
				try
				{
					EsFiveObject.prototype.newProperty= true;
				}
				catch (thrown) // if thrown instanceof TypeError
				{
					this.log(thrown);
					error= thrown;
				}
				
				assertFalse('The prototype is not extensible', Object.isExtensible(EsFiveObject.prototype));
				assertInstanceOf('A type error was thrown adding a new property to the prototype', TypeError, error);
				
				error= null;
				
				try
				{
					esObject= new EsFiveObject(value)
				}
				catch (thrown)
				{
					error= thrown;
					this.log(thrown);
				}
				
				assertNull('No error was thrown as a result of a value being set', error);
				assertInstanceOf('An instance was instantiated ok', EsFiveObject, esObject);
				assertTrue('Even though the prototpye is sealed, instances are extensible', Object.isExtensible(esObject));
				assertSame('esObject.property === value', value, esObject.property);
				assertSame('esObject.propertyValue === value', value, esObject.propertyValue);
				
				
			}
			
		,	testAccessorDescriptors: function ()
			{
				
				function Subject ()
				{
					
				};
				
				Subject.prototype= 
				{
					get constructor ()
					{
						return Subject;
					}
					
				,	set readAndWrite (value)
					{
						this.readAndWriteValue= value;
					}
					
				,	get readAndWrite ()
					{
						return this.readAndWriteValue;
					}
					
				,	get readOnly ()
					{
						return this.readOnlyValue;
					}
					
				,	set writeOnly (value)
					{
						this.writeOnlyValue= value;
					}
				};
				
				Object.defineProperties
				(
					Subject.prototype
					
				,	{
						data:
						{
							set: function (value)
							{
								this.dataValue= value;
							}
							
						,	get: function ()
							{
								return this.dataValue;
							}
						}
					}
				);
				
				Object.freeze(Subject.prototype);
				
				var subject= new Subject
				,	couldSet
				,	couldGet
				
				try
				{
					subject.constructor= Object;
					couldSet= true;
				}
				catch (thrown)
				{
					this.log(thrown);
					couldSet= false;
				}
				
				assertTrue('Subject.prototype is frozen', Object.isFrozen(Subject.prototype));
				assertSame('The constructor value was not updated', Subject, subject.constructor);
				assertFalse('The constructor value was not set', couldSet);
				
				
				SubSubject.prototype= Object.create(Subject.prototype);
				function SubSubject ()
				{
					
				};				
				
				couldSet= couldGet= void(0);
				
				try
				{
					// this will throw an error, not because Subject is frozen
					// but because Subject.prototype.constructor has only a getter!
					SubSubject.prototype.constructor= SubSubject;
					couldSet= true;
				}
				catch (thrown)
				{
					this.log(thrown);
					couldSet= false;
				}
				
				assertFalse('The SubSubject.prototype.constructor could be set', couldSet);
				assertSame('The SubjSubject constructor is Subject', Subject, new SubSubject().constructor);
				
				var descriptor= Object.getOwnPropertyDescriptor(SubSubject.prototype, 'constructor');
				
				assertUndefined('SubSubject does not define constructor, Subject does', descriptor);
				
				descriptor= Object.getOwnPropertyDescriptor(Subject.prototype, 'constructor');
				
				this.log(JSON.stringify(descriptor));
			}
			
			
		,	testAccessorDescriptors: function ()
			{
				
				function Subject () {;};
				
				Object.defineProperties
				(
					Subject.prototype
				
				,	{
						property:
						{
							set: function (value)
							{
								if (null == value)
									throw new TypeError('Must not be null');
									
								Object.defineProperty
								(
									this
									/* could we describe properties with UUIDS per instance?????? */
								,	'propertyValue'
								
								,	{
										value: value
									}
								);
							}
						
						,	get: function ()
							{
								return this.propertyValue;
							}
						}
					
					}
				);
				
				var subject= new Subject;
				subject.property= true;
				this.log(subject.property);
				this.log(subject.propertyValue);
				try
				{
					
				}
				catch (thrown)
				{
					
				}
				
			}
        }
    );
